Spring |
您所在的位置:网站首页 › onlyoffice api开发 › Spring |
只介绍集成 不介绍搭建环境 一、整体流程onlyoffice服务使用docker部署 docker-compose.yml 文件 version: '2' services: onlyoffice-documentserver: container_name: onlyoffice-documentserver image: sgcc-dky-smartky-onlyoffice_onlyoffice-documentserver depends_on: - onlyoffice-postgresql - onlyoffice-rabbitmq environment: - DB_TYPE=postgres - DB_HOST=onlyoffice-postgresql - DB_PORT=5432 - DB_NAME=onlyoffice - DB_USER=onlyoffice - AMQP_URI=amqp://guest:guest@onlyoffice-rabbitmq # Uncomment strings below to enable the JSON Web Token validation. #- JWT_ENABLED=true #- JWT_SECRET=secret #- JWT_HEADER=Authorization #- JWT_IN_BODY=true ports: - '8088:80' - '443:443' stdin_open: true restart: always stop_grace_period: 60s volumes: - /var/www/onlyoffice/Data - /var/log/onlyoffice - /var/lib/onlyoffice/documentserver/App_Data/cache/files - /var/www/onlyoffice/documentserver-example/public/files - /usr/share/fonts onlyoffice-rabbitmq: container_name: onlyoffice-rabbitmq image: rabbitmq restart: always expose: - '5672' ports: - '5672:5672' onlyoffice-postgresql: container_name: onlyoffice-postgresql image: postgres:9.5 environment: - POSTGRES_DB=onlyoffice - POSTGRES_USER=onlyoffice - POSTGRES_HOST_AUTH_METHOD=trust restart: always expose: - '5432' ports: - '5432:5432' volumes: - postgresql_data:/var/lib/postgresql volumes: postgresql_data: 访问文档时序图
OnlyOffice前端由onlyOffice-documentServer提供,后端会生成api.js文件,前端引入这个js就可以集成onlyOffice的编辑器。 前端项目结构
onlyofficeView是onlyoffice 基础组件 封装了核心的onlyoffice编辑器 import {handleDocType} from "./docType"; export default { name: "onlyofficeView", ...省略, methods: { initEditor() { // 加入 onlyoffice api.js 脚本 const apiScriptDom = document.getElementById("onlyoffice-document-api"); const script = document.createElement("script"); const {protocol} = window.location; const ONLYOFFICE_DOCUMENT_PORT=process.env.ONLYOFFICE_DOCUMENT_PORT; const ONLYOFFICE_DOCUMENT_URL=process.env.ONLYOFFICE_DOCUMENT_URL; script.setAttribute("id", "onlyoffice-document-api"); script.setAttribute( "src", `${this.documentUrl + ":" + ONLYOFFICE_DOCUMENT_PORT + ONLYOFFICE_DOCUMENT_URL}` ); document.body.appendChild(script); const _self = this; script.onload = () => { _self.scriptReady = true; console.log("initEditor 初始化组件完成!"); if (_self.option.url) { _self.setEditor(_self.option); } }; }, setEditor(option) { const { $store: { state: { user: {userId, nickname} } } } = this; this.doctype = handleDocType(option.fileType); } }, watch: { option: { handler: function (n, o) { this.doctype = handleDocType(n.fileType); if (this.scriptReady) { this.setEditor(n); } }, deep: true } } };onlineCat onlineEdit 是集成了onlyoffice编辑器的编辑和预览 主要是根据各种业务场景初始化的配置 三、后端集成核心方法 下载 download() 保存 save() 获取onlyoffice Token getToken() 校验token verifyToken() /** * onlyoffice 服务接口 * * @author: colagy * 2021-03-26 16:06 */ public interface IOnlyofficeService { /** * onlyoffice document server 下载文件 * @param id * @param scene * @param userId * @param timeStamp * @param token * @param response * @throws IOException */ void download(String id, Enum scene, String userId, Long timeStamp, String token, HttpServletResponse response) throws IOException; /** * 文件回写 * * @param request onlyoffce document server 请求 * @param eClass 文档场景枚举 类对象 * @param onlyofficeScene 文档场景枚举 * @return * @throws IOException */ JSONObject save(HttpServletRequest request, Class eClass) throws IOException; /** * @return */ OnlyofficeToken getToken(); boolean verifyToken(String userId, Long timeStamp, String token, Long expire); } 2.服务基类定义了两个 抽象方法 子类需要实现并返回 存储方法 和 token过期校验 @Service public abstract class SimpleOnlyofficeService implements IOnlyofficeService { private final static Logger log = LoggerFactory.getLogger(SimpleOnlyofficeService.class); private static final String tokenPrefix = "onlyoffice_token_profix_node_server"; public abstract IOnlyofficeStorage getOnlyofficeStorage(); public abstract long getTokenExpire(); ... }默认实现的download()方法 调用getOnlyofficeStorage 获取存储的实现类 可以是oss 或者本地文件存储 调用onlyofficeStorage.get()方法 返回值是inputStream写入响应流返回给onlyofficeDocumentServer @Override public void download(String id, Enum scene, String userId, Long timeStamp, String token, HttpServletResponse response) throws IOException { // token过期时间默认10分钟 long tokenExpire = getTokenExpire(); if (tokenExpire throw new CustomGenericException("授权已过期或授权失败"); } IOnlyofficeStorage onlyofficeStorage = getOnlyofficeStorage(); InputStream inputStream = onlyofficeStorage.get(id, scene); FileUtil.nioTransferTo(inputStream, response.getOutputStream()); // TODO 需要storage 返回更多信息 FileUtil.parseDownloadResponse(response, String.valueOf(timeStamp), "application/octet-stream"); }默认实现的save()方法 需要返回 {“error”:0} 给onlyoffice确认 调用 onlyofficeStorage.put(id, scene, inputStream, bytes.length)方法 将onlyOfficeServer传入的流保存 @Override public JSONObject save(HttpServletRequest request, Class eClass) throws IOException { Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A"); String bodyStr = scanner.hasNext() ? scanner.next() : ""; Map parameterMap = request.getParameterMap(); String[] ids = parameterMap.get("id"); String[] scenes = parameterMap.get("scene"); String id = null; if (ArrayUtils.isNotEmpty(ids)) { id = ids[0]; } if (Objects.isNull(id)) { JSONObject res = new JSONObject(); res.put("error", -1); return res; } Enum scene = null; if (ArrayUtils.isNotEmpty(scenes) && EnumUtils.isValidEnum(eClass, scenes[0])) { try { scene = Enum.valueOf(eClass, scenes[0]); } catch (Exception e) { String msg = "onlyoffice save方法 未指定scene"; log.error(ErrorUtil.getErrorStack(e, msg)); } } if (Objects.isNull(scene)) { JSONObject res = new JSONObject(); res.put("error", -1); return res; } JSONObject body = JSONObject.parseObject(bodyStr); int status = body.getInteger("status"); if (status == 2) { String downloadUrl = body.getString("url"); if (StringUtils.isNotBlank(downloadUrl)) { IOnlyofficeStorage onlyofficeStorage = getOnlyofficeStorage(); byte[] bytes = HttpUtil.downloadBytes(downloadUrl); InputStream inputStream = new ByteArrayInputStream(bytes); // 默认方法 可以实现beforeSave beforeSave(id, scene, parameterMap, body, inputStream); onlyofficeStorage.put(id, scene, inputStream, bytes.length); afterSave(id, scene, parameterMap, body, inputStream); } } // 正常处理需要返回 {"error":0} 给onlyoffice确认 JSONObject res = new JSONObject(); res.put("error", 0); return res; } 四、权限验证服务基类定义getUserId()方法 实现类需要返回一个用户唯一标识 /** * 必须实现返回当前登录用户id的方法 用于token获取以及校验 * * @return */ protected abstract String getUserId(); /** * 获取token * * @return */ @Override public OnlyofficeToken getToken() { String userId = getUserId(); long timeStamp = System.currentTimeMillis(); String tokenStr = parseToken(userId, timeStamp); OnlyofficeToken token = new OnlyofficeToken(userId, timeStamp, tokenStr); return token; } private String parseToken(String userId, Long timeStamp) { String tokenStr = "使用一些加密的方法将userId timeStamp 加密返回一个加密后的token,可以参加jwt"; return tokenStr; } /** * 校验token 并判断过期时间 * * @param token token * @param expire 过期时间(ms) 默认 10*60*000ms 10分钟 * @return */ @Override public boolean verifyToken(String userId, Long timeStamp, String token, Long expire) { if (StringUtils.isBlank(userId)) { log.warn("onlyofficeToken 验证失败 userId为空"); return false; } if (Objects.isNull(timeStamp) || timeStamp == 0) { log.warn("onlyofficeToken 验证失败 timeStamp为空"); return false; } if (Objects.isNull(expire)) { expire = 10 * 60 * 1000L; } long now = System.currentTimeMillis(); if (now - timeStamp > expire) { log.error("onlyoffice token已过期 , userId:" + userId + ", token:" + token + ", timeStamp:" + timeStamp + ", now:" + now + ", expire:" + expire); return false; } String realToken = parseToken(userId, timeStamp); boolean isValid = StringUtils.equals(token, realToken); if (!isValid) { log.error("onlyoffice token验证失败! " + " token:" + token + ", realToken:" + realToken + ", userId:" + userId + ", timeStamp:" + timeStamp + ", expire:" + expire); } return StringUtils.equals(token, realToken); } 五、储存集成 1.存储接口主要是两个方法 get() 返回字节流 put() 传入字节流 可以用oss实现 也可以用本地文件实现 只要能返回字节流和写入字节流就可以 /** * onlyoffice 在线编制的 储存方式需要实现这个接口 * * @author: colagy * 2021-03-29 11:03 */ public interface IOnlyofficeStorage { /** * 获取文件字节流 * * @param id 文件唯一标识 * @param scene 使用场景 * @return * @throws IOException */ InputStream get(String id, Enum scene) throws IOException; /** * 写入文件字节流 * * @param id 文件唯一标识 * @param scene 使用场景 * @param inputStream 输入流 * @param inSize 流大小(用于nio拷贝) * @throws IOException */ void put(String id, Enum scene, InputStream inputStream, long inSize) throws IOException; } 2.本地文件基类子类需要实现 getFilePath() 根据资源唯一标识返回文件的储存路径 /** * 文件系统存储抽象类 子类需要实现通过id获取存储路径的方法 * * @author: colagy * 2021-03-29 11:07 */ public abstract class FsStorage implements IOnlyofficeStorage { public abstract String getFilePath(String id, Enum scene); @Override public InputStream get(String id, Enum scene) throws IOException { String saveFilePath = getFilePath(id, scene); Assert.notBlank(saveFilePath); File file = new File(saveFilePath); File parentFile = file.getParentFile(); if (Objects.nonNull(parentFile)) { if (!parentFile.exists()) { parentFile.mkdirs(); } } if (!file.exists()) { file.createNewFile(); } Assert.isTrue(file.exists()); FileInputStream fileInputStream = new FileInputStream(file); return fileInputStream; } @Override public void put(String id, Enum scene, InputStream inputStream, long inSize) throws IOException { String filePath = getFilePath(id, scene); FileUtil.nioTransferTo(inputStream, inSize, filePath); } } 六、场景集成目前文件存储需要集成文件场景 不同的场景可以有不同的方式获取文件路径 /** * 场景需要实现这个接口 * * @author: colagy * 2021-06-11 17:10 */ public interface IFsScene { String getFilePath(String id); }文件场景默认实现类 base64方式 只做示例不推荐使用 /** * 文件路径base64 * * @author: colagy * 2021-06-15 12:03 */ @Service(value = "FS_BASE64") public class Base64FsScene implements IFsScene { /** * id为filePath的base64值 把base64解析就是filePath * TODO 前端base64需要使用 window.btoa(window.encodeURIComponent("/filepath/文档.doc")) 转base64 * * @param filePathBase64 filePath的base64值 * @return file path */ @Override public String getFilePath(String filePathBase64) { if (StringUtils.isBlank(filePathBase64)) { return ""; } try { Base64.Decoder decoder = Base64.getDecoder(); byte[] decode = decoder.decode(filePathBase64); String urlEncodeFilePath = new String(decode); String filePath = URLDecoder.decode(urlEncodeFilePath, "utf-8"); return filePath; } catch (Exception e) { return ""; } } } |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |